package sqlengine.conditions;

import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;

import sqlengine.database.DataFile;
import sqlengine.database.DataManager;
import sqlengine.groupby.ConditionAtom;
import sqlengine.groupby.ProjectionAtom;
import sqlengine.groupby.TableAtom;

// support only 2 tables
// support only VARCHAR2

public class Conditions {

	private boolean projectAll;
	String[] tableNames;
	int tableNum;
	DataFile firstDataFile, secondDataFile;
	ArrayList<ArrayList<String>> conditions;

	public boolean isProjectAll() {
		return projectAll;
	}

	public void setProjectAll(boolean projectAll) {
		this.projectAll = projectAll;
	}

	ArrayList<Projection> projections;
	
	public void addProjections(String tableName, String columnName, String title)
			throws ConditionExecption {
		if (tableNum == 1) {
			if (tableName.compareTo(tableNames[0]) != 0) {
				throw new ConditionExecption("can't find table: " + tableName);
			}
			if (firstDataFile.getSchema().containsKey(columnName) == false) {
				throw new ConditionExecption("can't find column: " + columnName
						+ " in table: " + tableName);
			}
			projections.add(new Projection(title, tableName, columnName));
//			firstTableProjectionColumns.add(columnName);
		} else {
			if (tableName.compareTo(tableNames[0]) == 0) {
				if (firstDataFile.getSchema().containsKey(columnName) == false) {
					throw new ConditionExecption("can't find column: "
							+ columnName + " in table: " + tableName);
				}
//				firstTableProjectionColumns.add(columnName);
				projections.add(new Projection(title, tableName, columnName));
			} else if (tableName.compareTo(tableNames[1]) == 0) {
				if (secondDataFile.getSchema().containsKey(columnName) == false) {
					throw new ConditionExecption("can't find column: "
							+ columnName + " in table: " + tableName);
				}
//				secondTableProjectionColumns.add(columnName);
				projections.add(new Projection(title, tableName, columnName));
			} else {
				throw new ConditionExecption("can't find table: " + tableName);
			}
		}
	}

	public ArrayList<Projection> getProjectionColumns() {
		return projections;
//		if (tableName.compareTo(tableNames[0]) == 0) {
//			return firstTableProjectionColumns;
//		} else {
//			return secondTableProjectionColumns;
//		}
	}

	public int getTableNum() {
		return tableNum;
	}

	public ArrayList<ArrayList<String>> getConditions() {
		return conditions;
	}

	public Conditions(String firstTableName, String secondTableName)
			throws ConditionExecption {
		if (DataManager.getOpenedFiles().containsKey(firstTableName) == false) {
			throw new ConditionExecption("can't find table: " + firstTableName);
		}
		if (DataManager.getOpenedFiles().containsKey(secondTableName) == false) {
			throw new ConditionExecption("can't find table: " + secondTableName);
		}
		firstDataFile = DataManager.getOpenedFiles().get(firstTableName);
		secondDataFile = DataManager.getOpenedFiles().get(secondTableName);
		projections = new ArrayList<Projection>();
		// two table
		tableNames = new String[2];
		tableNames[0] = firstTableName;
		tableNames[1] = secondTableName;
		tableNum = 2;
		conditions = new ArrayList<ArrayList<String>>();
		conditions.add(new ArrayList<String>());
		conditions.add(new ArrayList<String>());
		conditions.add(new ArrayList<String>());
		conditions.add(new ArrayList<String>());
		conditions.add(new ArrayList<String>());
	}

	public Conditions(String tableName) throws ConditionExecption {
		if (DataManager.getOpenedFiles().containsKey(tableName) == false) {
			throw new ConditionExecption("can't find table: " + tableName);
		}
		firstDataFile = DataManager.getOpenedFiles().get(tableName);
		projections = new ArrayList<Projection>();
		// one table
		tableNames = new String[1];
		tableNames[0] = tableName;
		tableNum = 1;
		conditions = new ArrayList<ArrayList<String>>();
		conditions.add(new ArrayList<String>());
		conditions.add(new ArrayList<String>());
	}

	public void addConditions(String lhsTableName, String lhsColumnName,
			String rhsTableName, String rhsColumnName)
			throws ConditionExecption {

		if (tableNum == 1) {
			// one table
			if (lhsTableName.compareTo(tableNames[0]) != 0) {
				throw new ConditionExecption("can't find table: "
						+ lhsTableName);
			}
			if (rhsTableName.compareTo(tableNames[0]) != 0) {
				throw new ConditionExecption("can't find table: "
						+ rhsTableName);
			}
			if (firstDataFile.getSchema().containsKey(lhsColumnName) == false) {
				throw new ConditionExecption("can't find column: "
						+ lhsColumnName + " in table: " + lhsTableName);
			}
			if (firstDataFile.getSchema().containsKey(rhsColumnName) == false) {
				throw new ConditionExecption("can't find column: "
						+ rhsColumnName + " in table: " + rhsTableName);
			}
			if (!lhsColumnName .equals( rhsColumnName)) {
				conditions.get(1).add(lhsColumnName);
				conditions.get(1).add(rhsColumnName);
			}
		} else {
			// two tables
			if (lhsTableName.compareTo(tableNames[0]) != 0
					&& lhsTableName.compareTo(tableNames[1]) != 0) {
				throw new ConditionExecption("can't find table: "
						+ lhsTableName);
			}
			if (rhsTableName.compareTo(tableNames[0]) != 0
					&& rhsTableName.compareTo(tableNames[1]) != 0) {
				throw new ConditionExecption("can't find table: "
						+ rhsTableName);
			}
			if (lhsTableName.compareTo(tableNames[0]) == 0) {
				if (firstDataFile.getSchema().containsKey(lhsColumnName) == false) {
					throw new ConditionExecption("can't find column: "
							+ lhsColumnName + " in table: " + lhsTableName);
				}
			} else {
				if (secondDataFile.getSchema().containsKey(lhsColumnName) == false) {
					throw new ConditionExecption("can't find column: "
							+ lhsColumnName + " in table: " + lhsTableName);
				}
			}
			if (rhsTableName.compareTo(tableNames[0]) == 0) {
				if (firstDataFile.getSchema().containsKey(rhsColumnName) == false) {
					throw new ConditionExecption("can't find column: "
							+ rhsColumnName + " in table: " + rhsTableName);
				}
			} else {
				if (secondDataFile.getSchema().containsKey(rhsColumnName) == false) {
					throw new ConditionExecption("can't find column: "
							+ rhsColumnName + " in table: " + rhsTableName);
				}
			}
			if (lhsTableName.equals(rhsTableName)) {
				if (lhsColumnName.equals(rhsColumnName)) {
					// same table, same column, eliminate
				} else {
					// same table, different columns
					if (lhsTableName.equals(tableNames[0])) {
						conditions.get(3).add(lhsColumnName);
						conditions.get(3).add(rhsColumnName);
					} else {
						conditions.get(4).add(lhsColumnName);
						conditions.get(4).add(rhsColumnName);
					}
				}
			} else {
				// two different table
				// add equal condition
				conditions.get(2).add(lhsColumnName);
				conditions.get(2).add(rhsColumnName);
			}
		}
	}

	public void addConditions(String tableName, String columnName, String value)
			throws ConditionExecption {
		if (tableNum == 1) {
			// one table
			if (tableName.compareTo(tableNames[0]) != 0) {
				throw new ConditionExecption("can't find table: " + tableName);
			}
			if (firstDataFile.getSchema().containsKey(columnName) == false) {
				throw new ConditionExecption("can't find column: " + columnName
						+ " in table: " + tableName);
			}
			conditions.get(0).add(columnName);
			conditions.get(0).add(value);
		} else {
			// two table
			if (tableName.compareTo(tableNames[0]) == 0) {
				if (firstDataFile.getSchema().containsKey(columnName) == false) {
					throw new ConditionExecption("can't find column: "
							+ columnName + " in table: " + tableName);
				}
			} else if (tableName.compareTo(tableNames[1]) == 0) {
				if (secondDataFile.getSchema().containsKey(columnName) == false) {
					throw new ConditionExecption("can't find column: "
							+ columnName + " in table: " + tableName);
				}
			} else {
				throw new ConditionExecption("can't find table: " + tableName);
			}

			if (tableName.equals(tableNames[0])) {
				conditions.get(0).add(columnName);
				conditions.get(0).add(value);
			} else {
				if (tableName.equals( tableNames[1])) {
					conditions.get(1).add(columnName);
					conditions.get(1).add(value);
				} else {
					throw new ConditionExecption(
							"add condition with wrong table Name");
				}
			}
		}
	}

	public String[] getTableNames() {
		return tableNames;
	}

	public String toString() {
		String retString = "";
		if (tableNum == 1) {
			retString = "There is " + tableNum + " table\n";
		} else {
			retString = "There are " + tableNum + " tables:\n";
			retString += tableNames[0] + "\n";
			retString += tableNames[1] + "\n";
		}

		if (projectAll) {
			retString += "Project column * ";
		} else {
//			int l = firstTableProjectionColumns.size();
//			retString += "first table project: \n";
//			for (int i = 0; i < l; ++i) {
//				retString += firstTableProjectionColumns.get(i) + "\n";
//			}
//			if (tableNum == 2) {
//				l = secondTableProjectionColumns.size();
//				retString += "second table project: \n";
//				for (int i = 0; i < l; ++i) {
//					retString += secondTableProjectionColumns.get(i) + "\n";
//				}
//			}
		}

		if (tableNum == 1) {
			for (int i = 0; i < 2; i++) {
				Iterator<String> it = conditions.get(i).iterator();
				while (it.hasNext()) {
					retString += it.next() + " ";
				}
				retString += "\n";
			}

		} else {
			for (int i = 0; i < 5; i++) {
				Iterator<String> it = conditions.get(i).iterator();
				while (it.hasNext()) {
					retString += it.next() + " ";
				}
				retString += "\n";
			}
		}

		return retString + "\n";
	}

}
